package com.lingxu.base.system.manage.service.impl;

import com.lingxu.base.common.api.vo.Result;
import com.lingxu.base.common.constant.CacheConstant;
import com.lingxu.base.common.constant.CommonConstant;
import com.lingxu.base.common.util.JwtUtil;
import com.lingxu.base.common.util.RedisUtil;
import com.lingxu.base.common.util.encrypt.MD5Utils;
import com.lingxu.base.system.api.ISysBaseAPI;
import com.lingxu.base.system.manage.entity.SysRole;
import com.lingxu.base.system.manage.entity.SysUser;
import com.lingxu.base.system.manage.mapper.SysMenuMapper;
import com.lingxu.base.system.manage.mapper.SysUserMapper;
import com.lingxu.base.system.manage.service.*;
import com.lingxu.base.system.manage.vo.LoginInfo;
import com.lingxu.base.system.manage.vo.LoginUser;
import lombok.extern.log4j.Log4j2;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Log4j2
@Service
public class LoginServiceImpl implements ILoginService {
    @Resource
    RedisUtil redisUtil;
    @Resource
    ISysBaseAPI sysBaseAPI;

    @Resource
    private ISysUserService sysUserService;
    @Resource
    private SysMenuMapper sysMenuMapper;
    @Resource
    private ISysUserRoleService sysUserRoleService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private GetMessage getMessage;
    @Resource
    private ISysUserMenuService sysUserMenuService;
    @Resource
    private ISysDepartService sysDepartService;


    @Resource
    private ISysMenuService sysMenuService;
    @Resource
    private SysUserMapper sysUserMapper;


    @Override
    public Result<LoginUser> checkStatus(SysUser sysUser) {
        Result<LoginUser> result = new Result<>();
        //情况1：根据用户信息查询，该用户不存在
        if (sysUser == null) {
            sysBaseAPI.addLog("该用户本系统不存在,请联系管理员!", 1,null,sysUser);
            result.error500("该用户本系统不存在,请联系管理员!");
            return result;
        }

        //情况2：根据用户信息查询，该用户已注销
        if ("1".equals(sysUser.getIsdel())) {
            sysBaseAPI.addLog("用户登录失败，用户名:" + sysUser.getUsername() + "已注销！", 1, null, sysUser);
            result.error500("该用户已注销");
            return result;
        }
        //情况3：根据用户信息查询，该用户已冻结
        if (CommonConstant.USER_FREEZE.toString().equals(sysUser.getStatus())) {
            sysBaseAPI.addLog("用户登录失败，用户名:" + sysUser.getUsername() + "已冻结！", 1, null, sysUser);
            result.error500("该用户已冻结");
            return result;
        }
        //情况3：根据用户信息查询，该用户未激活
        if (CommonConstant.USER_FREEZE.equals(sysUser.getIsuse())) {
            sysBaseAPI.addLog("用户登录失败，用户名:" + sysUser.getUsername() + "未激活！", 1,null,sysUser);
            result.error500("该用户已冻结");
            return result;
        }
        return result;
    }

    @Override
    public Result<?> resetPassword(List<String> ids) {
        for (String id : ids){
            SysUser sysUser = sysUserService.getById(id);
            SysUser user = new SysUser();
            user.setId(id);
            String password="MC5x.a4:";
            String passwordEncode= MD5Utils.Md5Encrypt(password);
            user.setPassword(passwordEncode);
            //最新密码修改时间
            user.setLastupdpwdtime(new Date());
            sysUserService.updateById(user);

            //推送T_SYS_USER
            sysUserService.updateTSysUser(id,passwordEncode);

            //删除因为密码错误而锁定用户信息
            DeleteMemory(getMessage.getLoginCountFailKey(sysUser));
            DeleteMemory(getMessage.getLoginTimeLockKey(sysUser));
            DeleteMemory(getMessage.getKeyName(sysUser));
        }
        return Result.ok("密码重置成功（重置31为：MC5x.a4:）!");
    }

    @Override
    public Result<LoginUser> checkPWDCorrect(String userNameInForm, String passwordInForm, SysUser sysUser) {
        String userpassword = sysUser.getPassword();
        String userpasswordnew = MD5Utils.Md5Encrypt(passwordInForm);

        if (!userpassword.equals(userpasswordnew)) {
            String valdate = loginValdate(sysUser);
            return Result.error(valdate);
        }
        DeleteMemory(getMessage.getLoginCountFailKey(sysUser));
        DeleteMemory(getMessage.getLoginTimeLockKey(sysUser));
        DeleteMemory(getMessage.getKeyName(sysUser));
        return Result.ok();
    }
    @Override
    public LoginInfo getLoginInfo(SysUser sysUser,String tokenId) {
        String userName = sysUser.getUsername();
        String password = sysUser.getPassword();
        String token;
        if(tokenId==null||"".equals(tokenId)){
            // 生成token
            token = JwtUtil.sign(userName, password);
        }else{
            token = tokenId;
        }
        // 设置token缓存有效时间
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
        redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, 60 * 30*2*1000);
        // 获取用户部门信息
        LoginInfo loginInfo=new LoginInfo();
        //起到隔离，去除部分参数的作用
        loginInfo.setToken(token);
        loginInfo.setUserInfo(sysUser);
        loginInfo.setSysDepart(sysDepartService.getById(sysUser.getDeptid()));
        String role = sysUserService.findRoleId(sysUser.getId());
        List<SysRole> sysRoles = sysUserRoleService.getSysRoleExistByUserid(sysUser.getId());
        loginInfo.setRoleList(sysRoles);
        Result existMenu = sysMenuService.findRoleExistMenu(role);
        List<com.lingxu.base.system.orgManage.entity.MenuTree> result = (List<com.lingxu.base.system.orgManage.entity.MenuTree>) existMenu.getResult();
        loginInfo.setMenuTreeList(result);

        return loginInfo;
    }

    //单独抽出，可以为该方法进行特殊操作
    //登陆时把SysBaseApiImpl所创建的shiro鉴权缓存清除
    @CacheEvict(value = CacheConstant.SYS_USERS_CACHE,key="#username")
    @Override
    public SysUser getUserByName(String username,String flag) {
        SysUser sysUser=sysUserService.getByName(username,flag);
        if(sysUser!=null){
            //清空用户登录Shiro权限缓存
            redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
            //清空用户的缓存信息（包括部门信息），例如sys:cache:user::<username>
            redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getName()));
        }
        return sysUser;
    }


    @Override
    public String getLoginName(String name) {
        return sysUserMapper.getLoginName(name);
    }


    /*=============================================10分钟内登录密码错误5次锁定账号30分钟==============================*/
    @Override
    public String loginValdata(SysUser user) {
        return null;
    }

    /**
     * 判断当前登录的用户是否被限制登录
     * 查询当前key是否存在,如果存在,就被限制,注意:需要给用户做提示:您当前的用户已被限制,还剩多长时间
     * 如果不存在就不被限制
     *
     * @param user
     * @return
     */
    @Override
    public Map<String, Object> loginUserLock(SysUser user) {
        Map<String, Object> map = new HashMap<>();
        if (stringRedisTemplate.hasKey(getMessage.getLoginTimeLockKey(user))) {
            //如果存在就是用户已经输错了密码五次 被锁定了30分钟
            map.put("flag", true);  //表示用户已经被锁定
            map.put("lockTime", stringRedisTemplate.getExpire(getMessage.getLoginTimeLockKey(user), TimeUnit.MINUTES));     //得到被锁定之后还剩下多少时间  以分钟返回
        } else {
            map.put("flag", false);   //flag 为false 表示用户没有被限制
        }
        return map;
    }


    /**
     * 登录失败的相应操作(密码错误)
     *
     * @param user
     * @return
     */

    @Override
    public String loginValdate(SysUser user) {

        int num = 3;
        //记录登录错误次数的key
        String key = getMessage.getLoginCountFailKey(user);
        if (!stringRedisTemplate.hasKey(key)) {   //如果不存在
            //是第一次登录失败 次数为1
            // userlogincountfile;用户名进行赋值   同时设置失效期10分钟
            stringRedisTemplate.opsForValue().set(key, "1", 5, TimeUnit.MINUTES);
            sysBaseAPI.addLog("用户登录失败，用户名:" + user.getUsername() + "密码错误,登录失败,在5分钟内还允许输入错误" + (num - 1) + "次", 1, null,user);
            return "登录失败,在5分钟内还允许输入错误" + (num - 1) + "次";
        } else {
            //如果存在
            //查询登录失败次数
            long loginFilCount = Long.parseLong(stringRedisTemplate.opsForValue().get(key));
            if (loginFilCount < (num - 1)) {    //代表当前如果登录次数小于4  意思:还有资格继续进行登录
                //登录次数+1
                stringRedisTemplate.opsForValue().increment(key, 1);
                long minutes = stringRedisTemplate.getExpire(key, TimeUnit.MINUTES);  //返回的是秒
                sysBaseAPI.addLog("用户登录失败，用户名:" + user.getUsername() + "密码错误,登录失败,在5分钟内还允许输入错误" + (num - 1) + "次", 1, null,user);
                return user.getUsername() + "登录失败,在" + minutes + "分钟内还允许输入错误" + (num - loginFilCount - 1) + "次";
            } else {   //超过了指定的登录次数
                String lockkey = getMessage.getLoginTimeLockKey(user);
                stringRedisTemplate.opsForValue().set(lockkey, "1", 30, TimeUnit.MINUTES);
                return "因登录失败此时超过" + num + "次,以对其限制登录30分钟";
            }
        }
    }

    @Override
    public Boolean DeleteMemory(String key) {
        try {
            stringRedisTemplate.delete(key);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /*=============================================10分钟内登录密码错误5次锁定账号30分钟==============================*/

}
